﻿//  Copyright (c) Microsoft Corporation. All rights reserved.

using System;
using System.IO;
using System.Transactions;

namespace Microsoft.ServiceModel.Samples
{
    static class TransactionMessageBuffer
    {
        /// <summary>
        /// Generates a buffer in which it puts the lenght of transaction's propagation token, 
        /// the propagation token, and lastly the message bytes. If the transaction propagation token 
        /// is null, we insert 4 null bytes at the beginning of the buffer.       
        /// </summary>
        public static byte[] WriteTransactionMessageBuffer(byte[] txPropToken, ArraySegment<Byte> message)
        {            
            // start writing all the info into a memory buffer
            MemoryStream mem = new MemoryStream();

            // copy the bytes encoding the length of the txPropToken            
            // first get the bytes representing the length of the txPropToken
            byte[] txLengthBytes;

            if (txPropToken != null)
            {
                txLengthBytes = BitConverter.GetBytes(txPropToken.Length);
            }
            else
            {
                txLengthBytes = BitConverter.GetBytes((int)0);
            }
            mem.Write(txLengthBytes, 0, txLengthBytes.Length);

            // copy the bytes of the transaction propagation token
            if (txPropToken != null)
            {
                mem.Write(txPropToken, 0, txPropToken.Length);
            }

            // copy the message bytes
            mem.Write(message.Array, message.Offset, message.Count);

            return mem.ToArray();
        }

        /// <summary>
        /// Reads out a transaction and a message from a byte buffer.        
        /// The layout of the buffer should conform with that generated by WriteTransactionMessageBuffer.
        /// This method can throw:
        ///     ArgumentNullException - if 'buffer' is null
        ///     ArgumentException     - if 'count' is less than sizeof(int)
        ///     TransactionException  - if TransactionInterop.GetTransactionFromTransmitterPropagationToken fails
        ///     InvalidDataException  - if the length of the transaction propagation token is negative or greater 
        ///                             than the length of the data in the buffer
        ///     
        /// </summary>
        public static void ReadTransactionMessageBuffer(byte[] buffer, int count, out Transaction transaction, out ArraySegment<byte> message)
        {
            const byte sizeOfInt = sizeof(int);            

            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");                
            }
            if(count < sizeOfInt)
            {
                throw new ArgumentException("'count' is less than size of on int.");
            }

            MemoryStream mem = new MemoryStream(buffer, 0, count, false, true);          

            // read the length of the transaction token
            byte[] txLengthBytes = new byte[sizeOfInt];
            mem.Read(txLengthBytes, 0, sizeOfInt);
            int txLength = BitConverter.ToInt32(txLengthBytes, 0);

            // check the validity of the length of the transaction propagation token
            if (txLength >= count - sizeOfInt || txLength < 0)
            {
                throw new InvalidDataException("the length of the transaction propagation token read from 'buffer' is invalid");
            }

            transaction = null;
            // read the transaction propagation token and unmarshal the transaction                        
            if (txLength > 0)
            {
                byte[] txToken = new byte[txLength];
                mem.Read(txToken, 0, txToken.Length);

                transaction = TransactionInterop.GetTransactionFromTransmitterPropagationToken(txToken);
            }

            // read the message
            int offset = (int)mem.Position;
            message = new ArraySegment<byte>(mem.GetBuffer(), offset, count - offset);             
        }       
    }
}
